/****************************************************************************
**
** Copyright (C) 2013 Atmel Corporation and/or its subsidiary(-ies).
** All rights reserved.
**
** LIMITED License Agreement
**
** IT IS IMPORTANT THAT YOU READ THIS AGREEMENT CAREFULLY AND COMPLETELY.
** This Limited License Agreement (“Agreement”) is a legally binding agreement between,
** on one hand, either your employer (if you are acting on behalf of your employer)
** or you (if you are acting on your own behalf) (“Licensee”), and on the other hand,
** Atmel Corporation (“Atmel”).
**
** By installing or using any of the software downloaded or provided with these
** terms (“Licensed Software”), you are indicating that you are binding Licensee
** to the terms of this Agreement, and that you are duly authorized by Licensee to do so.
** If you are not authorized to bind Licensee to the terms of this Agreement,
** or if Licensee does not agree to be bound by all of the terms of this Agreement,
** do not install or use any such software.
**
**
** 1.	Grant of License.  Subject to the terms and conditions of this Agreement,
** Atmel grants Licensee a non-exclusive, non-transferable, non-sublicensable,
** limited license to use the Licensed Software solely in connection with Atmel products
** from SAM9 and SAMA5 families (“Atmel Product”).
**
** 2.	Restrictions.  Except as expressly set forth in Section 1, Licensee will not,
** and will have no right to, (a) use, copy or reproduce any Licensed Software except,
** (b) modify, create derivative works of, sell, distribute, transfer or disclose any
** Licensed Software. Without limiting the generality of the foregoing, Licensee will not,
** and will have no right to, use any Licensed Software for any semiconductor products
** that are not Atmel Products. Licensee will not remove, obscure or alter any trademark,
** copyright or other proprietary rights or ownership notices of Atmel that appear
** in any Licensed Software.
**
** 3.  	Title.  As between the parties, Atmel retains full rights, title, and ownership
** including all patents, copyrights, trade secrets, trade names, trademarks, and other
** intellectual property rights in and to the Licensed Software.
**
** 4.  	No Other Rights.  Except as expressly stated herein, this Agreement does not grant
** Licensee any rights to patents, copyrights, trade secrets, trade names, trademarks
** (whether registered or unregistered), or any other rights, franchises, or licenses
** in respect of the Licensed Software.
**
** 5.	Disclaimer of Warranty and No Support.  ALL LICENSED SOFTWARE IS PROVIDED “AS IS”,
** “WITH ALL FAULTS”, AND WITH NO WARRANTY WHATSOEVER.  ATMEL EXPRESSLY DISCLAIMS ALL
** WARRANTIES OF ANY KIND (WHEHTER EXPRESS, IMPLIED, STATUTORY OR OTHERWISE) IN CONNECTION
** WITH THE LICENSED SOFTWARE OR ANY OTHER ASPECT OF THIS AGREEMENT, INCLUDING, WITHOUT
** LIMITATION ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
** TITLE OR NON-INFRINGEMENT OF THIRD PARTY RIGHTS, AND ANY WARRANTIES THAT MAY ARISE
** FROM COURSE OF DEALING, COURSE OF PERFORMANCE OR USAGE OF TRADE.  ATMEL WILL HAVE
** NO OBLIGATION UNDER THIS AGREEMENT TO CORRECT ANY BUGS, DEFECTS OR ERRORS IN
** THE LICENSED SOFTWARE, PROVIDE ANY UPDATES, UPGRADES OR NEW RELEASES OF THE LICENSED SOFTWARE,
** OR OTHERWISE PROVIDE ANY SUPPORT OR MAINTENANCE FOR THE LICENSED SOFTWARE.
**
** 6.  	Export.  Licensee will comply with all applicable laws and regulations of all
** relevant jurisdictions in connection with its activities related to the Licensed Software.
** Without limitation of the foregoing, Licensee acknowledges that certain laws
** and regulations of the United States and other jurisdictions may pertain to the export
** and re-export of the Licensed Software, and Licensee will not export or re-export
** any Licensed Software in any form without the appropriate governmental approvals,
** or otherwise in violation of any such laws or regulations.
**
** 7.	Termination.  The license will automatically terminate if Licensee fails to comply
** with any of the terms and conditions of the license including, without limitation
** the confidentiality obligation or the obligations set forth in Sections 1, 2, 6, 7, 10
** and 11 herein. Upon termination for any reason, Licensee will immediately destroy
** or return to Atmel all whole or partial copies of the Licensed Software.
**
** 8.	High Risk Activities. LICENSEE ACKNOWLEDGES AND AGREES THAT THE LICENSED SOFTWARE
** IS NOT DESIGNED OR APPROVED FOR, AND WILL NOT BE INCORPORATED (WITHOUT THE EXPRESS WRITTEN
** APPROVAL OF AN OFFICER OF ATMEL) INTO, ANY PRODUCTS THAT ARE USED OR DESIGNED TO BE USED
** IN CONNECTION WITH ANY ACTIVITIES WHERE THE FAILURE OF SUCH PRODUCTS COULD REASONABLY
** BE EXPECTED TO RESULT IN DEATH, BODILY INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
** ("HIGH RISK ACTIVITIES").  IN NO EVENT WILL ATMEL HAVE ANY LIABILITY TO LICENSEE
** OR ANY THIRD PARTY ARISING OUT OF OR RELATED TO ANY USE OF LICENSED SOFTWARE IN CONNECTION
** WITH HIGH RISK ACTIVITIES, AND ATMEL HEREBY DISCLAIMS ANY EXPRESS OR IMPLIED WARRANTY OF
** FITNESS FOR ANY HIGH RISK ACTIVITIES.
**
** 9.  	LIMITATION OF LIABILITY.  TO THE EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT WILL
** ATMEL BE LIABLE TO LICENSEE OR ANY THIRD PARTY (WHETHER SUCH LIABILITY IS BASED ON CONTRACT,
** NEGLIGENCE, STRICT LIABILITY, OTHER TORT THEORY, CONTRIBUTION, BREACH OF WARRANTY,
** OR OTHER LEGAL OR EQUITABLE THEORY) FOR ANY SPECIAL, INDIRECT, INCIDENTAL, EXEMPLARY,
** PUNITIVE OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES FOR LOSS OF PROFITS,
** LOSS OR INTERRUPTION OF BUSINESS, OR LOSS OF DATA, ARISING OUT OF OR RELATING TO
** THE LICENSED SOFTWARE OR ANY OTHER ASPECT OF THIS AGREEMENT, EVEN IF ATMEL has been advised
** of or should have known of the possibility of such damages.
**
** IN NO EVENT WILL ATMEL’S TOTAL LIABILITY ARISING OUT OF OR RELATED TO THE LICENSED SOFTWARE
** OR ANY OTHER ASPECT OF THIS AGREEMENT (WHETHER UNDER CONTRACT, NEGLIGENCE, STRICT LIABILITY,
** CONTRIBUTION, BREACH OF WARRANTY, OR OTHER LEGAL OR EQUITABLE THEORY) EXCEED THE GREATER OF
** (A) THE AGGREGATE OF ALL LICENSE FEES PAID BY LICENSEE TO ATMEL FOR THE LICENSED SOFTWARE, OR
** (B) ONE THOUSAND DOLLARS ($1,000). WITHOUT LIMITING THE FOREGOING,
** AND Notwithstanding any provision herein to the contrary, ATMEL will not be liable for
** ANY costs of PROCURING SUBSTITUTE GOODS, SERVICES OR TECHNOLOGY under any circumstances.
**
** Each Party acknowledges that THE OTHER PARTY has entered into this agreement in reliance
** on the limitations of liability, DISCLAIMERS OF WARRANTIES, EXCLUSION OF DAMAGES
** AND EXCLUSIVE REMEDIES contained in this AGREEMENT, AND THAT EACH OF THE FOREGOING PROVISIONS FORMS
** AN ESSENTIAL AND fundamental part of the basis of the bargain BETWEEN THE PARTIES,
** WITHOUT WHICH SUCH the other Party would not have entered into this Agreement.
** EACH PARTY AGREES THAT SUCH PROVISIONS WILL SURVIVE AND APPLY NOTWITHSTANDING ANY
** FAILURE OF ESSENTIAL PURPOSE OF ANY LIMITED REMEDY OR LIMITATION OF LIABILITY.
**
** 10.  	Governing Law; Dispute Resolution. This Agreement is to be construed in accordance
** with and governed by the internal laws of the State of California (as permitted by Section 1646.5
** of the California Civil Code or any similar successor provision), without giving effect
** to any choice of law rule that would cause the application of the laws of any jurisdiction
** other than the internal laws of the State of California to the rights and duties of the parties.
** This Agreement will not be governed by the U.N. Convention on the Sale of Goods,
** the application of which is expressly excluded. Except for actions for injunctive
** or other equitable relief, which may be brought in any court of competent jurisdiction,
** all disputes arising out of or related to this Agreement will be subject to the exclusive
** jurisdiction of the California state courts in Santa Clara County, California,
** or if there is exclusive federal jurisdiction, the United States District Court for
** the Northern District of California, and the Parties hereby consent to, and agree to submit to,
** the personal and exclusive jurisdiction and venue of such courts.
**
** 11. 	General. Licensee will not, and will have no right to, assign, delegate or otherwise
** transfer (whether voluntarily, by operation of law or otherwise) this Agreement or any of
** its rights or obligations hereunder to any third party without the prior written consent
** of Atmel, and any purported assignment, delegation or other transfer without such consent
** will have no force or effect. Subject to the foregoing, this Agreement will be binding upon
** and will inure to the benefit of the parties and their respective successors and permitted assigns.
** No failure of either party to enforce any right under this Agreement will be deemed a waiver
** of such right or any other right under this Agreement. Any waiver by a party of a breach
** of any provision of this Agreement by the other party hereunder will not be deemed to be
** a waiver of any subsequent breach of such provision or a waiver of any breach of any other
** provision of this Agreement.
** This Agreement may not be superseded, modified, or amended except in a writing signed by an officer
** of each party. If any provision of this Agreement is determined to be invalid, illegal
** or otherwise unenforceable, such provision will be enforced to the extent possible consistent
** with the intent of the parties, and the remaining provisions of this Agreement will remain in
** full force and effect. This Agreement will be fairly interpreted in accordance with its terms
** and without any strict construction against either party because it was drafted by such party
** or for any other reason. This Agreement will constitute the entire agreement between the parties
** relating to the subject matter hereof, and expressly supersedes and replaces all prior
** and contemporaneous agreements, proposals, quotations, negotiations and communications, written
** or oral, between the parties relating to such subject matter.
**
**
** Software Author: Timesys Corporation (www.timesys.com)
**
** Summary: Top class for the temperature control Widget
**
****************************************************************************/

#include "temperaturecontrol.h"
#include "ui_temperaturecontrol.h"
#include <QTimer>
#include <QFile>
#include <QPainter>
#include <QtCore/qmath.h>
#include <QMouseEvent>

//-- probably should not be here
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
//--

extern bool isSmallResolution;
int TemperatureControl::sensitivity = 4;
float TemperatureControl::unitsSensivity[3] = {1, 1.8, 1}; // 0=F, 1=C, 2=%

/***
 ** Default constructor
 ** Connects signals for controls with handling functions
 ** Loads custom stylesheet
 **/
TemperatureControl::TemperatureControl(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::TemperatureControl),
    mViewTimer( new QTimer(this)),
    currAngle(0),
    prevAngle(0)
{
    ui->setupUi(this);
    QString stylePath;
    if (isSmallResolution) {
        stylePath = ":/resources/temp-dial-stylesheet-small.qss";
        image_red = QPixmap(":/resources/temp-dial-red-small.png");
        image_blue = QPixmap(":/resources/temp-dial-blue-small.png");
        image_teal = QPixmap(":/resources/temp-dial-teal-small.png");
        image_knob = QPixmap(":/resources/knob-small.png");
        mRadius = 105;
        mKnobCenterX = 24;
        mKnobCenterY = 22;
        ui->verticalLayout->setContentsMargins(64,64,64,64);
        ui->verticalLayout->setSpacing(4);
    } else {
        stylePath = ":/resources/temp-dial-stylesheet.qss";
        image_red = QPixmap(":/resources/temp-dial-red.png");
        image_blue = QPixmap(":/resources/temp-dial-blue.png");
        image_teal = QPixmap(":/resources/temp-dial-teal.png");
        image_knob = QPixmap(":/resources/knob-big.png");
        mRadius = 132;
        mKnobCenterX = 30;
        mKnobCenterY = 27;
    }
    //all images suppouse to have the same dimension
    mImageWidth  = image_blue.width();
    mImageHeight = image_blue.height();
    setMinimumSize( mImageWidth, mImageHeight);

    mViewTimer->setInterval( 3000); //3s
    mViewTimer->setSingleShot( true);
    connect( mViewTimer, SIGNAL(timeout()), this, SLOT(resetView()));
    setupDial();
    devMode = 0;

    QFile qss( stylePath);
    qss.open(QFile::ReadOnly);
    this->setStyleSheet(qss.readAll());
    qss.close();
}

TemperatureControl::~TemperatureControl()
{
    delete ui;
}

QSize TemperatureControl:: sizeHint() const
{
    return QSize(mImageWidth, mImageHeight);
}

/***
 ** Initial Dial setup
 ** One can change dial sensitivity - default = 2
 **/
void TemperatureControl::setupDial()
{
    //init everything;
    currentTemperature = 64;
    targetTemperature = 71;
    displayUnits = 0;
    adjustingNow = false;
    sameTemps = false;
    setHeatingCooling(0);
    //assign initial values
    ui->label_Temp_Top->setText(getValue(currentTemperature));
    ui->label_Temp_Bottom->setText(getValue(targetTemperature));
}

/***
 ** Convert the temperature to appropriate value given
 ** selected by user metric - convert string C/F
 **/
QString TemperatureControl::getValue(int temp)
{
    int temperature;
    QChar ch(0xB0);

    if(displayUnits == 1)
        temperature = (temp-32)/1.8;
    else
        temperature = temp;

    QString qStr = QString::number(temperature);
    if(devMode != 2){
        if(displayUnits == 0){
            qStr.append(ch);
            qStr.append("F");
        }else if(displayUnits == 1){
            qStr.append(ch);
            qStr.append("C");
        }
    }else{
        if(displayUnits == 1){
            if(temperature>100){
                qStr = QString("100");
                targetTemperature = temp;
            }else if(temp < 32){
                qStr = QString("0");
                targetTemperature = 32;
            }
        }else{
            if(temp>100){
                qStr = QString("100");
                targetTemperature = 100;
            }else if(temp < 0){
                qStr = QString("0");
                targetTemperature = 0;
            }
        }
        qStr.append("%");
    }
    return qStr;
}

/***
 ** Set the display Units mode
 **/
void TemperatureControl::setMode(int mode)
{
    displayUnits = mode;
    adjustingTemperature();
    mViewTimer->start();
}

/***
 ** Public function that allows switching of the dial decoration and features
 ** between Cooling/Heating/Humidity
 **/
void TemperatureControl::setHeatingCooling(int deviceMode)
{
    devMode = deviceMode;
    switch (devMode) {
    case 0:
        background = &image_red;
        break;
    case 1:
        background = &image_blue;
        break;
    case 2:
        background = &image_teal;
        break;
    }
    adjustingTemperature();
    mViewTimer->start();
    update();
}

/***
 ** Swap the temps when adjusting temperature
 **/
void TemperatureControl::adjustingTemperature()
{
    if(sameTemps){
        sameTemps = false;
        ui->label_Bottom->setVisible(true);
        ui->label_Temp_Bottom->setVisible(true);
    }

    if(!adjustingNow)
    {
        QString tempString;

        //swap labels
        tempString = ui->label_Top->text();
        ui->label_Top->setText(ui->label_Bottom->text());
        ui->label_Bottom->setText(tempString);

        //swap temps
        tempString = ui->label_Temp_Top->text();
        ui->label_Temp_Top->setText(ui->label_Temp_Bottom->text());
        ui->label_Temp_Bottom->setText(tempString);

        adjustingNow = true;
    }
    ui->label_Temp_Bottom->setText(getValue(currentTemperature));
    ui->label_Temp_Top->setText(getValue(targetTemperature));
}

/***
 ** Resets the current temp to be on top
 **/
void TemperatureControl::resetView()
{
    if(adjustingNow)
    {
        QString tempString;

        //swap labels
        tempString = ui->label_Top->text();
        ui->label_Top->setText(ui->label_Bottom->text());
        ui->label_Bottom->setText(tempString);

        //swap temps
        tempString = ui->label_Temp_Top->text();
        ui->label_Temp_Top->setText(ui->label_Temp_Bottom->text());
        ui->label_Temp_Bottom->setText(tempString);

        adjustingNow = false;
    }

    //if the temperatures are the same, remove the set point
    if (currentTemperature == targetTemperature)
    {
        ui->label_Bottom->setVisible(false);
        ui->label_Temp_Bottom->setVisible(false);
        sameTemps = true;
    }

    if(devMode == 0){
        if(currentTemperature < targetTemperature)
            deviceOn(1);
        else
            deviceOff(1);
    }else{
        if(currentTemperature > targetTemperature)
            deviceOn(0);
        else
            deviceOff(0);
    }

}


/***
 ** GPIO access routine that control 2 GPIO connected LEDs
 ** This method turns specific LED ON
 **/
void TemperatureControl::deviceOn(bool heat)
{
    int devfd;
    char buffer [10];
    char device [11];

    if(heat)
    {
        printf("Furnace is On\n");
        sprintf(device,"/leds/led1");
    }else{
        printf("Air Conditioner is On\n");
        sprintf(device,"/leds/led2");
    }

    if((devfd = ::open(device,O_WRONLY)) < 0)
    {
        if(errno ==EBUSY)
            qDebug("Device already in use\n");
    }else{
        printf("Writing to device\n");
        sprintf(buffer,"%d", 1);
        ::write(devfd,buffer,1);
    }

    ::close(devfd);

    //TODO: Add your own handling code here
}

/***
 ** GPIO access routine that control 2 GPIO connected LEDs
 ** This method turns specific LED OFF
 **/
void TemperatureControl::deviceOff(bool heat)
{
    int devfd;
    char buffer [10];
    char device [11];

    if(heat)
    {
        printf("Furnace is Off\n");
        sprintf(device, "/leds/led1");
    }else{
        printf("Air Conditioner is Off\n");
        sprintf(device, "/leds/led2");
    }

    if((devfd = ::open(device,O_WRONLY)) < 0)
    {
        if(errno ==EBUSY)
            qDebug("Device already in use\n");
    }else{
        sprintf(buffer,"%d", 0);
        ::write(devfd, buffer, 1);
    }
    ::close(devfd);

    //TODO: Add your own handling code here
}

void TemperatureControl::paintEvent(QPaintEvent *)
{
    QPainter painter(this);
    painter.drawPixmap( (width() - mImageWidth) / 2,
                        (height() - mImageHeight) / 2,
                        *background);

    painter.translate( width() / 2, height() / 2); //set origin in the middle
    qreal angle = 6.282 / 360 * currAngle; //convert value to radians
    painter.drawPixmap(  mRadius * qSin(angle) - mKnobCenterX, //radius is 132 (based on image), 30 is knob radius
                        -mRadius * qCos(angle) - mKnobCenterY, //as above, minus because of y axis
                        image_knob);
}

/***
 ** Capture touch (mouse) events to calculate new knob position and to
 ** generate signals to control external device
 **/
void TemperatureControl::mouseMoveEvent(QMouseEvent * event)
{
    if (event->buttons() & Qt::LeftButton)
    {
        QLineF zeroLine( width() / 2, height() / 2, width() / 2, 0);
        QLineF currentLine( width() / 2, height() / 2, event->x(), event->y());
        currAngle = currentLine.angleTo( zeroLine);

        //fix discontinuity with 60 degree accuracy
        if (currAngle > 330  && prevAngle < 30)
            prevAngle += 360;
        if (currAngle < 30  && prevAngle > 330)
            prevAngle -= 360;

        int diff = (currAngle - prevAngle) / sensitivity / unitsSensivity[displayUnits];
        if (diff) {
            prevAngle = currAngle;
            targetTemperature += diff;
            adjustingTemperature();
        }
        update();
    }
}

/***
 ** When the user adjusts temperature, target temp is displayed on top
 ** Kick-off, restart 3sec timer each time user stops temp adjustment
 **/
void TemperatureControl::mouseReleaseEvent(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton)
        mViewTimer->start();
}
